`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: Portland State University
// Engineer: Rohit Kulkarni, Raviraj Kokil
// 
// Create Date: 01/14/2015 01:34:44 PM
// Design Name: Hardware Pulse Width Detect module
// Module Name: HW_PWD
// Project Name: ECE544 project 2
// Target Devices: Artix 7
// Tool Versions: Vivado 2014.4
// Description: This file describes the Hardware approach of pulse width detection
//              Pulse width detection is done using a sampling clock of a frequency at least 10 times more than the input 
//              pulse width modulated(PWM) signal. The number of samples during the high time and the low time of the PWM signal
//              enable to determine the frequency and the duty cycle of the PWM signal which is done in the Microblaze soft core.
//              
//              Duty Cycle =   high_count / (high_count + low_count) 
//   
//              Frequency  =   sampling clock frequency / (high_count + low_count)             
//
// Dependencies: None
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments: Is able to determine the frequency and the duty cycle with a resolution of 1% for maximum frequency of 1MHz
// 
//////////////////////////////////////////////////////////////////////////////////

module HW_PWD(
    input clk_HW_PWD,           //input clock to sample
    input pwm_in,               //PWM output from AXI timer
    input reset,                //reset to the HW_PWD wired to the system reset

    output reg update_count,        //interrupt to Microblaze to update count
    output reg [31:0] low_timer,    //low_timer count
    output reg [31:0] high_timer    //high_timer count
);
   
   //Parameters for state values
   parameter RESET      =   5'b00001; 
   parameter INITIALIZE =   5'b00010;
   parameter HIGH_COUNT =   5'b00100;
   parameter LOW_COUNT  =   5'b01000;
   parameter INTERRUPT  =   5'b10000;
   
     
    reg pwm_in_p;                       //pulse width modulated signal PREVIOUS value 
    reg pwm_in_c;                       //pulse width modulated signal CURRENT value
    reg zero_one_tran = 0;              //This signals goes high for 1 clock cycle when there is a 0 -> 1 transition
    reg one_zero_tran = 0;              //This signals goes high for 1 clock cycle when there is a 1 -> 0 transition
    reg [4:0] Present_State;            //Stores the Present state of the state machine 
    reg [4:0] Next_State;               //Stores the Next state of the state machine
    reg [31:0] high_timer_local = 0;    //Local high timer count which gets reset to 0 on a 0 -> 1 transition
    reg [31:0] low_timer_local = 0;     //Local low timer count which gets reset to 0 on a 1 -> 0 transition
    reg high_timer_en = 0;              //Enable to increment high count timer register			
    reg low_timer_en = 0;               //Enable to increment low count timer register
    reg high_timer_rst = 0;             //Reset to make high count timer set to 0 	        
    reg low_timer_rst = 0;	            //Reset to make low count timer set to 0 
    
    //Update the variable to the new PWM count signal  
    always @(posedge clk_HW_PWD)
    begin
        pwm_in_p    <=  pwm_in_c;       //Latch the previous value of the input PWM signal in a register
        pwm_in_c    <=  pwm_in;         //Latch the current value of the input PWM signal in a register
    end
    
    //Detect 0 -> 1 and 1 -> 0 transition here
    always @(posedge clk_HW_PWD)
    begin
        if(pwm_in_p != pwm_in_c)                            //Checks if the previous value is not equal to the current value  
        begin                                               //indicates a transition
            if(pwm_in_p == 1'b0 && pwm_in_c == 1'b1)
            begin
                zero_one_tran <= 1'b1;                      //For a 0 -> 1 transition
                one_zero_tran <= 1'b0;
            end
            else
            begin
                one_zero_tran <= 1'b1;                      //For a 1 -> 0 transition
                zero_one_tran <= 1'b0;
            end                
        end
        else
        begin
            one_zero_tran <= 1'b0;                          //For no transition
            zero_one_tran <= 1'b0;                       
        end
    end
    
    
    //State machine for pulse width detection
    
    //Update State 
    always @(posedge clk_HW_PWD)
    begin
        if(reset)
            Present_State   <=  RESET;
        else
            Present_State   <=  Next_State;                                   
    end
    
    //Output State logic
    always @(*)
    begin
        case(Present_State)
            RESET:      begin
                            update_count        = 1'b0;                            
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en	    = 1'b0;	
                            low_timer_en	    = 1'b0;
                            high_timer_rst	    = 1'b1;
                            low_timer_rst	    = 1'b1;
                        end
                      
            INITIALIZE: begin                                       //initialize high count and low count to 0 since 0 -> 1 pulse has occurred
                            update_count        = 1'b0;                                                                                      
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
            			    high_timer_en	    = 1'b0;	
                            low_timer_en	    = 1'b0;
                            high_timer_rst      = 1'b1;
                            low_timer_rst       = 1'b1;	
                        end  
            
            HIGH_COUNT: begin                                       //assert high timer enable for incrementing high count
                            update_count        = 1'b0;                                                                                                                  
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                                              
                            high_timer_en       = 1'b1;	
                            low_timer_en        = 1'b0;	
                            high_timer_rst      = 1'b0;
                            low_timer_rst       = 1'b1;
                        end
                  
            LOW_COUNT:  begin                                       //assert low timer enable for incrementing low count
                            update_count        = 1'b0;                                                                                                                              
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en       = 1'b0;	
                            low_timer_en        = 1'b1;	
                            high_timer_rst      = 1'b0;
                            low_timer_rst       = 1'b0;
                        end
              
            INTERRUPT:  begin                                       //latch the counts before reseting them to 0
                            update_count        = 1'b1;             //also interrupt Microblaze to give the counts   
                            low_timer           = low_timer_local;
                            high_timer          = high_timer_local;                                                                                                                                                                  
                            high_timer_en       = 1'b0;	
                            low_timer_en        = 1'b0;
                            high_timer_rst      = 1'b0;
                            low_timer_rst       = 1'b0;	
                        end
                                 
            default:    begin
                            update_count        = 1'b0;                                                                                                                                                                  
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en       = 1'b0;	
                            low_timer_en        = 1'b0;	
                            high_timer_rst      = 1'b1;
                            low_timer_rst       = 1'b1;	
                        end
        endcase               
    end
    
    //Next state logic
    always @(*)
    begin
        case (Present_State)
        RESET:      begin
                        if(one_zero_tran)
                    	   Next_State  =  INITIALIZE;              //Stay in reset until a 1 -> 0 transition occurs
                    	else
                    	   Next_State  =  RESET;                                                                
                    end
          
        INITIALIZE: begin
                        if(zero_one_tran)
                            Next_State  =  HIGH_COUNT;             //start high count if 0 -> 1 transition occurs
                         else
                            Next_State  =  INITIALIZE;                                                                                       
                    end  
        
        HIGH_COUNT: begin
                        if(one_zero_tran)                          //start low count if 1 -> 0 transition occurs
                            Next_State  =  LOW_COUNT;
                        else
                            Next_State  =  HIGH_COUNT;    
                    end 
              
        LOW_COUNT:  begin
                        if(zero_one_tran)
                            Next_State  =  INTERRUPT;               //interrupt microblaze if 0 -> 1 transition occurs
                        else
                            Next_State  =  LOW_COUNT;                                            
                    end
          
        INTERRUPT:  begin                                          //Interrupt needs to be active for 1 clock cycle since operating at 100MHz
                        Next_State  =  RESET;
                    end
                        
        default:    begin
                        Next_State  =  INITIALIZE;
                    end     
       endcase
    end  

	//Manage high timer here
	always @(posedge clk_HW_PWD)
	begin
	    if(high_timer_en)
	    begin
	        high_timer_local 	<= high_timer_local + 1'b1;      //Increment high timer if high timer enable signal is high
	    end		
	    else if(high_timer_rst)
    		high_timer_local 	<= 32'b0;	                     //Set high timer count to 0 if high timer reset signal is high
	    else
	        high_timer_local 	<= high_timer_local;             //If none, then keep the value
    end	            
   
    
	//Manage low timer here
	always @(posedge clk_HW_PWD)
	begin
	    if(low_timer_en)
	    begin
	        low_timer_local 	<= low_timer_local + 1'b1;    //Increment low timer if low timer enable signal is high
	    end		
	    else if(low_timer_rst)
    		low_timer_local 	<= 32'b0;	                  //Set low timer count to 0 if low timer reset signal is high
	    else
	        low_timer_local 	<= low_timer_local;           //If none, then keep the value
    end	            


endmodule